﻿/**
 * @file coverwidgetheaderview.cpp
 * @author huangxb
 * @email 18228033207@163.com
 * @date 2024.04.07
 * @license MIT License
 * @brief 通过自定义widget覆盖视图项所在位置来显示自定义控件.
 * 视图项自定义widget父窗口使用视图viewport()函数返回的widget，
 * 这样视图自绘时也会绘制其子窗口，达到视图项显示自定义widget的效果。
 * 而自定义视图的位置是通过实现QHeaderView，在其paint函数中获取该
 * 视图项当前位置，然后对自定义widget使用setGeometry显示在该位置
 * （相当于盖住了原视图项的内容），达到视图显示自定义控件的功能。
 *
 * @htmlonly
 * <span style="font-weight: bold">History</span>
 * @endhtmlonly
 * Version|Auther|Date|Describe
 * ------|----|------|--------
 * V1.0|huangxb|2024.04.07|创建
 */

/* 自定义头文件 */
#include "coverwidgetheaderview.h"

#include <QApplication>
#include <QEvent>
#include <QMouseEvent>

CCoverWidgetHeaderView::CCoverWidgetHeaderView(Qt::Orientation orientation, QWidget *parent)
    : QHeaderView(orientation, parent)
{

}

void CCoverWidgetHeaderView::SetWidget(int logicalIndex, QWidget *pWidget)
{
    auto find = m_mapHeaderWidget.find(logicalIndex);
    if(find != m_mapHeaderWidget.end())
    {
        auto pOldWidget = find.value();
        delete pOldWidget;
    }

    /* 设置控件父节点*/
    pWidget->setParent(this);
    /*
     * 由于控件覆盖在当前视图头前面，会让后面的视图接收不到事件，因此这里进行事件拦截，
     * 并将事件传播到视图
     */
    pWidget->installEventFilter(this);

    m_mapHeaderWidget[logicalIndex] = pWidget;
}

QWidget *CCoverWidgetHeaderView::GetWidget(int logicalIndex) const
{
    auto find = m_mapHeaderWidget.find(logicalIndex);
    return find != m_mapHeaderWidget.end() ? find.value() : nullptr;
}

QWidget *CCoverWidgetHeaderView::TakeWidget(int logicalIndex)
{
    auto find = m_mapHeaderWidget.find(logicalIndex);
    if(find == m_mapHeaderWidget.end())
    {
        return nullptr;
    }

    auto pWidget = find.value();
    pWidget->removeEventFilter(this);
    m_mapHeaderWidget.erase(find);

    return pWidget;
}

void CCoverWidgetHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
{
    /* 绘制默认视图头样式 */
    QHeaderView::paintSection(painter, rect, logicalIndex);

    /* 获取当前表头正在显示的索引 */
    auto rcView = viewport()->rect();
    /* 横向表头左侧可见的索引或纵向表头顶部可见的索引 */
    int indexLeftOrTop = logicalIndexAt(0,0);
    /* 横向表头右侧可见的索引或纵向表头底部可见的索引 */
    int indexRightOrBottom = logicalIndexAt(rcView.right(), rcView.bottom());

    /* 横向表头左侧不可见的索引或纵向表头顶部不可见的索引 */
    for(int i=0;i<indexLeftOrTop;i++)
    {
        auto find = m_mapHeaderWidget.find(i);
        if(find != m_mapHeaderWidget.end())
        {
            find.value()->hide();
        }
    }
    int nMaxCount = orientation() == Qt::Horizontal ? model()->columnCount() : model()->rowCount();
    /* 横向表头右侧不可见的索引或纵向表头底部不可见的索引 */
    for(int i=indexRightOrBottom+1;i<nMaxCount;i++)
    {
        auto find = m_mapHeaderWidget.find(i);
        if(find != m_mapHeaderWidget.end())
        {
            find.value()->hide();
        }
    }

    /* 无自定义控件则直接返回 */
    auto find = m_mapHeaderWidget.find(logicalIndex);
    if(find == m_mapHeaderWidget.end())
    {
        return;
    }

    /* 有自定义控件则显示该控件并设置位置 */
    auto pWidget = find.value();
    pWidget->show();
    pWidget->setGeometry(rect);
}

bool CCoverWidgetHeaderView::eventFilter(QObject *object, QEvent *event)
{
    /*
     * 因为自定义控件挡住了视图的item，所以鼠标事件会发送的自定义控件，导致视图无法响应鼠标事件
     * 因此拦截到自定义控件的鼠标事件后再转发到视图上，让视图进行相应处理
     */
    if(event->type() == QEvent::MouseButtonRelease
        || event->type() == QEvent::MouseButtonPress
        || event->type() == QEvent::MouseMove)
    {
        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
        auto pWidget = qobject_cast<QWidget*>(object);
        auto ptfLocal = mouseEvent->localPos();
        QPoint ptLocal;
        ptLocal.setX(ptfLocal.x());
        ptLocal.setY(ptfLocal.y());
        auto ptParent = pWidget->mapToParent(ptLocal);
        QMouseEvent newEvent(mouseEvent->type(), ptParent, mouseEvent->screenPos(),
                             mouseEvent->button(), mouseEvent->buttons(), mouseEvent->modifiers());
        QApplication::sendEvent(viewport(), &newEvent);
    }

    /* 返回false，让自定义控件处理自己的事件 */
    return false;
}
